package com.github.chen.orm.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.env.EnvironmentCapable;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 类路径扫描
 */
@Slf4j
public class ClassPathScanner {

    private ResourcePatternResolver resourcePatternResolver;
    private Environment environment;
    private MetadataReaderFactory metadataReaderFactory;
    private static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    public ClassPathScanner(BeanDefinitionRegistry registry) {
        ResourceLoader resourceLoader = registry instanceof ResourceLoader ? (ResourceLoader) registry : null;
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
        this.environment = ((DefaultListableBeanFactory) registry).getBean(Environment.class);
    }

    public Set<Class<?>> scan(String... basePackage) {
        return Arrays.stream(basePackage)
                .map(this::scan)
                .flatMap(Collection::stream).collect(Collectors.toSet());
    }


    public Set<Class<?>> scan(String basePackage) {
        Set<Class<?>> set = new LinkedHashSet<>();
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                resolveBasePackage(basePackage) + '/' + DEFAULT_RESOURCE_PATTERN;
        try {
            Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
                    String className = metadataReader.getClassMetadata().getClassName();
                    Class<?> clazz;
                    try {
                        clazz = Class.forName(className);
                        set.add(clazz);
                    } catch (ClassNotFoundException e) {
                        log.error("扫描包发生错误：", e);
                    }
                }
            }
        } catch (IOException e) {
            log.error("扫描包发生错误：", e);
        }
        return set;
    }

    protected String resolveBasePackage(String basePackage) {
        return ClassUtils.convertClassNameToResourcePath(this.getEnvironment().resolveRequiredPlaceholders(basePackage));
    }

    private Environment getEnvironment() {
        return this.environment;
    }
}
